package com.atguigu.daijia.dispatch.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.result.Result;
import com.atguigu.daijia.dispatch.mapper.OrderJobMapper;
import com.atguigu.daijia.dispatch.service.NewOrderService;
import com.atguigu.daijia.map.client.LocationFeignClient;
import com.atguigu.daijia.model.entity.dispatch.OrderJob;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.vo.dispatch.NewOrderTaskVo;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.order.NewOrderDataVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import netscape.javascript.JSObject;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.atguigu.daijia.dispatch.xxl.client.XxlJobClient;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class NewOrderServiceImpl implements NewOrderService {

    @Autowired
    private OrderJobMapper orderJobMapper;
    @Autowired
    private XxlJobClient xxlJobClient;
    @Autowired
    private LocationFeignClient locationFeignClient;
    @Autowired
    private OrderInfoFeignClient orderInfoFeignClient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Long addAndStartTask(NewOrderTaskVo newOrderTaskVo) {
        //判断订单是否启动任务调度
        LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderJob::getOrderId,newOrderTaskVo.getOrderId());
        OrderJob orderJob = orderJobMapper.selectOne(wrapper);
        if (orderJob != null){
            //已经启动
            return orderJob.getJobId();
        }
        Long jobId = xxlJobClient.addAndStart("newOrderTaskHandler", "", "0 0/1 * * * ?", "新创建订单任务调度：" + newOrderTaskVo.getOrderId());
        OrderJob job = new OrderJob();
        job.setOrderId(newOrderTaskVo.getOrderId());
        job.setJobId(jobId);
        job.setParameter(JSONObject.toJSONString(newOrderTaskVo));
        orderJobMapper.insert(job);
        return job.getJobId();
    }

    @Override
    public void executeTask(long jobId) {
        LambdaQueryWrapper<OrderJob> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderJob::getJobId, jobId);
        OrderJob orderJob = orderJobMapper.selectOne(wrapper);
        if (orderJob == null) {
            return;
        }
        String jsonString = orderJob.getParameter();
        NewOrderTaskVo newOrderTaskVo = JSONObject.parseObject(jsonString, NewOrderTaskVo.class);
        Result<Integer> orderStatusResult = orderInfoFeignClient.getOrderStatus(orderJob.getOrderId());
        Integer orderStatus = orderStatusResult.getData();
        if (orderStatus != 1) {
            xxlJobClient.startJob(jobId);
            return;
        }
        SearchNearByDriverForm searchNearByDriverForm = new SearchNearByDriverForm();
        searchNearByDriverForm.setLongitude(newOrderTaskVo.getStartPointLongitude());
        searchNearByDriverForm.setLatitude(newOrderTaskVo.getStartPointLatitude());
        searchNearByDriverForm.setMileageDistance(newOrderTaskVo.getExpectDistance());
        List<NearByDriverVo> nearByDriverVoList = locationFeignClient.searchNearByDriver(searchNearByDriverForm).getData();
        for (NearByDriverVo nearByDriverVo : nearByDriverVoList) {
            String key = RedisConstant.DRIVER_ORDER_REPEAT_LIST + newOrderTaskVo.getOrderId();
            Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, nearByDriverVo.getDriverId().toString());
            if (!isMember){
                stringRedisTemplate.opsForSet().add(key,nearByDriverVo.getDriverId().toString());
                stringRedisTemplate.expire(key,RedisConstant.ORDER_ACCEPT_MARK_EXPIRES_TIME, TimeUnit.MINUTES);

                NewOrderDataVo newOrderDataVo = new NewOrderDataVo();
                newOrderDataVo.setOrderId(newOrderTaskVo.getOrderId());
                newOrderDataVo.setStartLocation(newOrderTaskVo.getStartLocation());
                newOrderDataVo.setEndLocation(newOrderTaskVo.getEndLocation());
                newOrderDataVo.setExpectAmount(newOrderTaskVo.getExpectAmount());
                newOrderDataVo.setExpectDistance(newOrderTaskVo.getExpectDistance());
                newOrderDataVo.setExpectTime(newOrderTaskVo.getExpectTime());
                newOrderDataVo.setFavourFee(newOrderTaskVo.getFavourFee());
                newOrderDataVo.setDistance(nearByDriverVo.getDistance());
                newOrderDataVo.setCreateTime(newOrderTaskVo.getCreateTime());

                key = RedisConstant.DRIVER_ORDER_TEMP_LIST + nearByDriverVo.getDriverId();
                stringRedisTemplate.opsForList().leftPush(key,JSONObject.toJSONString(newOrderDataVo));
                stringRedisTemplate.expire(key,RedisConstant.DRIVER_ORDER_TEMP_LIST_EXPIRES_TIME,TimeUnit.MINUTES);
            }

        }
    }

    @Override
    public List<NewOrderDataVo> findNewOrderQueueData(Long driverId) {
        String key = RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId;
        List<NewOrderDataVo> newOrderDataVoList = new ArrayList<>();
        Long size = stringRedisTemplate.opsForList().size(key);
        if (size <= 0){
            return Collections.emptyList();
        }
        for (int i = 0; i < size; i++) {
            String pop = stringRedisTemplate.opsForList().leftPop(key);
            newOrderDataVoList.add(JSONObject.parseObject(pop,NewOrderDataVo.class));
        }
        return newOrderDataVoList;
    }

    @Override
    public Boolean clearNewOrderQueueData(Long driverId) {
        Boolean delete = stringRedisTemplate.delete(RedisConstant.DRIVER_ORDER_TEMP_LIST + driverId);
        return BooleanUtils.isTrue(delete);
    }
}
